package org.lds.hotkey.worker.holder;

import io.etcd.jetcd.KeyValue;
import io.etcd.jetcd.Watch;
import io.etcd.jetcd.watch.WatchEvent;
import lombok.extern.slf4j.Slf4j;
import org.lds.hotkey.common.autoconfigure.etcd.EtcdService;
import org.lds.hotkey.common.autoconfigure.spring.SpringUtil;
import org.lds.hotkey.common.constant.KeyConstant;
import org.lds.hotkey.common.enums.ErrorEnum;
import org.lds.hotkey.common.model.Entity;
import org.lds.hotkey.common.model.RuleModel;
import org.lds.hotkey.common.util.AssertUtil;
import org.lds.hotkey.common.util.EntityModelUtil;
import org.lds.hotkey.common.util.JsonUtil;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class RuleHolder {
    private static final EtcdService etcdService = SpringUtil.getBean(EtcdService.class);

    /**
     * key: ruleId
     */
    private static final Map<Long, RuleModel> RULE_MAP = new ConcurrentHashMap<>();

    public static void start() {
        init();
        watch();
    }

    /**
     * 初始化 RULE_MAP
     */
    private static void init() {
        List<KeyValue> keyValues = etcdService.listByPrefix(KeyConstant.RULE_DIRECTORY);
        AssertUtil.assertTrue(Objects.nonNull(keyValues), ErrorEnum.ETCD_UNAVAILABLE);

        for (KeyValue keyValue : keyValues) {
            Entity<RuleModel> entity = EntityModelUtil.getEntity(keyValue, RuleModel.class);
            if (Objects.nonNull(entity)) {
                RULE_MAP.put(entity.getValue().getId(), entity.getValue());
            }
        }

        log.info("RULE_MAP={}", JsonUtil.toJson(RULE_MAP));
    }

    private static void watch() {
        Watch.Watcher watcher = etcdService.watchKey(KeyConstant.RULE_DIRECTORY, watchResponse -> {
            for (WatchEvent event : watchResponse.getEvents()) {
                Entity<RuleModel> entity = EntityModelUtil.getEntity(event, RuleModel.class);
                if (Objects.isNull(entity)) continue;

                if (event.getEventType() == WatchEvent.EventType.PUT) {
                    RULE_MAP.put(entity.getValue().getId(), entity.getValue());
                } else {
                    RULE_MAP.remove(entity.getValue().getId());
                }
            }
            log.info("RULE_MAP={}", JsonUtil.toJson(RULE_MAP));
        });
        AssertUtil.assertTrue(Objects.nonNull(watcher), ErrorEnum.ETCD_UNAVAILABLE);
    }

    public static RuleModel getRuleModel(Long ruleId) {
        if (ruleId == null) return null;
        return RULE_MAP.get(ruleId);
    }
}
